#####################################################################################
# Copyright (c) 2011, Mihails Prihodko, Daniels Umanovskis, Aleksejs Zaharovs.      #
# All rights reserved.                                                              #
# Program is licensed under the BSD license - full license in license.txt           #
#####################################################################################

import json
import os
import sys
import packer
from ctypes import *
import pickle
import gc
import random

items = []
methods = []

def main():
    print ("Knappsack algorithm system here\n")
    gc.enable()
    load_methods()
    command_loop()

def load_methods():
    global methods
    if (os.path.exists("methods.dat")):
        with open("methods.dat", "r") as f:
            methods = pickle.load(f)

def save_methods():
    with open("methods.dat", "w") as f:
        pickle.dump(methods, f)
    
def command_loop():
    print ("Enter command\n")
    done = False
    global items
    while not done:
        sys.stdout.write(">")
        command = raw_input()
        command = command.split()
        if (len(command) == 0):
            continue
        if command[0] == "load":
            if (len(command) <> 2):
                print "Usage: load filename"
            else:
                if not(os.path.exists(command[1])):
                    print "File " + str(command[1]) + " does not exist\n"
                else:
                    f = open(command[1], "r")
                    items = json.loads(f.read())
                    f.close()
        elif command[0] == "clearitems":
            items = []
        elif command[0] == "removeitem":
            if (len(command) <> 2):
                print "Usage: removeitem item-name\n"
            else:
                for item in items:
                    if (item["name"] == command[1]):
                        items.remove(item)
                        break
        elif command[0] == "additem":
            #additem knife 120 10
            if (len(command) <> 4):
                print "Usage: additem name weight price\n"
            else:
                items.append({"name":command[1], "weight":int(command[2]), "price":int(command[3])})
        elif command[0] == "showitems":
            showitems(items)
            #for i in range(len(items)):
            #    print items[i]["name"]
        elif command[0] == "encode":
            print json.dumps(items, indent=4)
        elif command[0] == "save":
            if (len(command) <> 2):
                print "Usage: save filename\n"
            else:
                if (os.path.exists(command[1])):
                    #print "File " + str(command[1]) + " does not exist\n"
                    pass
                else:
                    f = open(command[1], "w")
                    #print json.loads(f.read())
                    f.write(json.dumps(items))
                    f.close()
        elif command[0] == "addmethod":
            #addmethod name libname
            if (len(command) <> 3):
                print "Usage: addmethod method-name library-name"
            else:
                methods.append([command[1], command[2]])
                save_methods()
        elif command[0] == "removemethod":
            if (len(command) <> 2):
                print "Usage: removemethod method-name\n"
            else:
                for item in methods:
                    if (item[0] == command[1]):
                        methods.remove(item)
                        break
        elif command[0] == "showmethods":
            showmethods()
        elif command[0] == "execute":
            #execute method-name size infile outfile
            if (len(command) <> 5):
                print "Usage: execute method-name size infile outfile\n"
            else:
                if command[1] == "genetic":
                    command = command[1:]
                    output = genetic(command)
                    showitems(output)
                elif command[1] == "branches":
                    command = command[1:]
                    output = branches(command)
                    showitems(output)
                else:
                    method = None
                    for m in methods:
                        if (m[0] == command[1]):
                            method = m
                            break
                    if (method == None):
                        print "No such method\n"
                        break
                    output = execute(method, command)             
                    showitems(output)
        elif command[0] == "genetic":
            if (len(command) <> 4):
                print "Usage: genetic size infile outfile\n"
            else:
                output = genetic(command)    
                showitems(output)
        elif command[0] == "branches":
            if (len(command) <> 4):
                print "Usage: branches size infile outfile\n"
            else:
                output = branches(command)    
                showitems(output)
        elif command[0] == "compare":
            #uses current
            #compare method1 method2 size
            if (len(command) <> 4):
                print "Usage: compare method1 method2 size"
            compare(command)
        elif command[0] == "generate":
            if (len(command) <> 3) and (len(command) <> 2):
                print "Usage: generate amount [filename]"
            else:
                if (len(command) == 2):
                    generate(int(command[1]), "random.js")
                else:
                    generate(int(command[1]), command[2])
        elif command[0] == "help":
            showhelp(command)
        elif command[0] == "quit":
            done = True

def genetic(l):
    if (l[0] == "execute"):
        l = l[1:]
    s = int(l[1])
    if (l[2] == "current"):
        with open("current.js", "w") as f:
            f.write(json.dumps(items))
        l[2] = "current.js"
    with open(l[2], "r") as f:
        knap = json.loads(f.read())
    output = packer.genetic(knap, s) #capacity
    with open(l[3], "w") as f:
        f.write(json.dumps(output))    
    return output

def branches(l):
    s = int(l[1])
    if (l[2] == "current"):
        with open("current.js", "w") as f:
            f.write(json.dumps(items))
        l[2] = "current.js"
    with open(l[2], "r") as f:
        knap = json.loads(f.read())
    output = packer.bb(knap, s) #capacity
    with open(l[3], "w") as f:
        f.write(json.dumps(output))    
    return output

def execute(l, c):
    gc.collect()
    if not (os.path.exists(l[1])):
        print "Library " + str(l[1]) + " not found"
        return None
    if (c[3] == "current"):
        with open("current.js", "w") as f:
            f.write(json.dumps(items))
            f.close()
        #print str(f.closed)
        c[3] = "current.js"
    lib = WinDLL(l[1])
    s = int(c[2])
    lib.pack(s, c[3], c[4])
    with open(c[4], "r") as f:
        try:
            output = json.loads(f.read())
        except:
            output = "[]"
            print f.read()
    gc.collect()
    return output

def compare(l):
    method = None
    #print l
    #execute method 200 pool.js out.js
    c = ["execute", l[1], l[3], "current", "compare.dat"]
    if not (l[1] == "genetic") and not (l[1] == "branches"):
        for m in methods:
            if (m[0] == l[1]):
                method = m
                break
        if (method == None):
            print "No such method " + str(l[1]) + "\n"       
        output = execute(method, c)
    elif l[1] == "genetic":
        #print c
        output = genetic(c)
    elif l[1] == "branches":
        output = branches(c)

    showitems(output)
    #os.remove("current.js")
    c2 = ["execute", l[2], l[3], "current", "compare.dat"]
    if not (l[2] == "genetic") and not (l[2] == "branches"):
        for m in methods:
            if (m[0] == l[2]):
                method2 = m
                #print method2
        if (method2 == None):
            print "No such method " + str(l[2]) + "\n"       
        output2 = execute(method2, c2)
    elif l[2] == "genetic":
        output2 = genetic(c2)
    elif l[2] == "branches":
        output = branches(c2)      
    
    showitems(output2)
                
def showhelp(l):
    if (len(l) == 1):
        print "Command list:\nload\nsave\nadditem\nremoveitem\nshowitems\naddmethod\nremovemethod\nshowmethods\nexecute\ncompare"
    if (len(l) == 2):
        if l[1] == "load":
            print "Load a knapsack from a JSON file"
            print "Usage: load filename"
        elif l[1] == "save":
            print "Save the current knapsack to a JSON file"
            print "Usage: save filename"
        elif l[1] == "showitems":
            print "Show items in the current knapsack"
            print "Usage: showitems"
        elif l[1] == "additem":
            print "Adds an item with the specified name, weight and price"
            print "Usage: additem name weight price"
        elif l[1] == "removeitem":
            print "Removes the first item with the specified name"
            print "Usage: removeitem name"
        elif l[1] == "clearitems":
            print "Empties the current knapsack"
            print "Usage: clearitems"
        elif l[1] == "showitems":
            print "Shows the current knapsack"
            print "Usage: showitems"
        elif l[1] == "showmethods":
            print "Shows the available packing algorithms"
            print "Usage: showmethods"
        elif l[1] == "addmethod":
            print "Adds a packing algorithm. Second parameter is the DLL file"
            print "Usage: addmethod method-name libraryname"
            print "Ex: addmethod myalgorithm myalg.dll"
        elif l[1] == "removemethod":
            print "Removes a packing algorithm by name"
            print "Usage: removemethod method-name"
        elif l[1] == "showmethods":
            print "Shows available packing algorithms"
            print "Usage: showmethods"
        elif l[1] == "execute":
            print "Executes a packing algorithm"
            print "First parameter is name of the algorithm"
            print "Second parameter is the maximum size of the knapsack"
            print "Third and fourth parameters are input and output files"
            print "Keyword 'current' can be used instead of the input file name, to use the current knapsack"
            print "Usage: execute method-name size infile outfile"
        elif l[1] == "genetic":
            print "Executes the genetic packing algorithm, short-hand for execute genetic"
            print "Parameters are maximum knapsack size, input file and output file"
            print "Usage: genetic size infile outfile"
        elif l[1] == "branches":
            print "Executes the branches & bounds method, short-hand for execute branches"
            print "Parameters are maximum knapsack size, input file and output file"
            print "Usage: branches size infile outfile"
        elif l[1] == "compare":
            print "Compares two algorithms. First two parameters are algorithm names, followed by maximum size of the knapsack"
            print "Uses the current knapsack in memory"
            print "Usage: compare method-name1 method-name2 size"
        elif l[1] == "quit":
            print "Quits the system"
        elif l[1] == "help":
            print "No recursion for you, bad boy!"


def showitems(l):
    if (l == None):
        return
    totalprice = 0.0
    totalweight = 0.0
    template = "{name:15}|{weight:6}|{price:6}"
    print template.format(name="NAME", weight="WEIGHT", price="PRICE")
    for item in l:
        #print item
        totalprice += float(item["price"])
        totalweight += float(item["weight"])
        print template.format(**item)
    print ("TOTAL: Weight " + str(totalweight) + ", price " + str(totalprice))

def showmethods():
    template = "{0:15}|{1:15}"
    print template.format("NAME", "LIBRARY")
    for item in methods:
        #print item
        print template.format(*item)
    print "genetic        | builtin"
    print "branches       | builtin"

def generate(count, fname):
    names = ["gun", "big-gun", "food", "medkit", "KLM", "torch", "ammo",
             "book", "sword", "knife", "battery", "map", "potion", "cigarettes",
             "water", "tea", "lemon", "haircomb", "compass", "shampoo",
             "crossbow", "baton", "armor", "dagger", "fork", "antirad", "umbrella",
             "crowbar", "gluon-gun", "nuka-cola", "pipboy", "bozar", "gatling-laser",
             "power-armor", "iguana-stick", "spellbook"]
    randitems = []
    random.seed()
    for i in range(count):
        rnd = random.randint(0, len(names) - 1)
        randitems.append({"name":names[rnd], "weight":random.randint(1, 300), "price":random.randint(10, 3500)})
        with open(fname, "w") as f:
            f.write(json.dumps(randitems))

        
        
if __name__ == "__main__":
    main()
